/*
 * Copyright (C) 2013 Allwinnertech, kevin.z.m <kevin@allwinnertech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <linux/clk-private.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/clk/sunxi.h>
#include <mach/sys_config.h>
#include "clk-sunxi.h"
#include "clk-factors.h"
#include "clk-periph.h"
#include "clk-sun8iw8.h"
#include "clk-sun8iw8_tbl.c"

#ifndef CONFIG_EVB_PLATFORM
    #define LOCKBIT(x) 31
#else
    #define LOCKBIT(x) x
#endif
static DEFINE_SPINLOCK(clk_lock);
void __iomem *sunxi_clk_base;
void __iomem *sunxi_clk_cpus_base=0;
int	sunxi_clk_maxreg =SUNXI_CLK_MAX_REG;
int cpus_clk_maxreg = 0;
#ifdef CONFIG_SUNXI_CLK_DUMMY_DEBUG
unsigned int dummy_reg[1024];
unsigned int dummy_readl(unsigned int* regaddr)
{
	unsigned int val;
	val = *regaddr;
	printk("--%s-- dummy_readl to read reg 0x%x with val 0x%x\n",__func__,((unsigned int)regaddr - (unsigned int)&dummy_reg[0]),val);
	return val;
}
void  dummy_writel(unsigned int val,unsigned int* regaddr)
	{
		*regaddr = val;
		printk("--%s-- dummy_writel to write reg 0x%x with val 0x%x\n",__func__,((unsigned int)regaddr - (unsigned int)&dummy_reg[0]),val);
	}

void dummy_reg_init(void)
{
	memset(dummy_reg,0x0,sizeof(dummy_reg));
	dummy_reg[PLL1_CFG/4]=0x00001000;
	dummy_reg[PLL2_CFG/4]=0x00035514;
	dummy_reg[PLL3_CFG/4]=0x03006207;
	dummy_reg[PLL4_CFG/4]=0x03006207;
	dummy_reg[PLL5_CFG/4]=0x00001000;
	dummy_reg[PLL6_CFG/4]=0x00041811;
	dummy_reg[PLL8_CFG/4]=0x03006207;
	dummy_reg[PLL9_CFG/4]=0x03001300;
	dummy_reg[PLL10_CFG/4]=0x03006207;
	dummy_reg[CPU_CFG/4]=0x00001000;
	dummy_reg[AHB1_CFG/4]=0x00001010;
	dummy_reg[APB2_CFG/4]=0x01000000;
	dummy_reg[ATS_CFG/4]=0x80000000;
	dummy_reg[PLL_LOCK/4]=0x000000FF;
	dummy_reg[CPU_LOCK/4]=0x000000FF;
}
#endif // of CONFIG_SUNXI_CLK_DUMMY_DEBUG

/*                            			ns  nw  ks  kw  ms  mw  ps  pw  d1s  d1w  d2s  d2w  {frac  out  mode}  en-s   sdmss  sdmsw  sdmpat      sdmval*/
SUNXI_CLK_FACTORS_DELAY(	pll_cpu,    8,  5,  4,  2,  0,  2,  16, 2,  0,   0,   0,   0,    0,    0,   0,     31,   24,     0,       PLL_CPUPAT,  0xd1303333 , 10);
SUNXI_CLK_FACTORS(			pll_audio,  8,  7,  0,  0,  0,  5,  16, 4,  0,   0,   0,   0,    0,    0,   0,     31,   24,     1,       PLL_AUDIOPAT,0xc0010d84);
SUNXI_CLK_FACTORS(			pll_video,  8,  7,  0,  0,  0,  4,  0,  0,  0,   0,   0,   0,    1,    25,  24,    31,   20,     0,       PLL_VIDEOPAT,0xd1303333);
SUNXI_CLK_FACTORS(			pll_ve,     8,  7,  0,  0,  0,  4,  0,  0,  0,   0,   0,   0,    1,    25,  24,    31,   20,     0,       PLL_VEPAT,   0xd1303333);
SUNXI_CLK_FACTORS_UPDATE(	pll_ddr0,   8,  5,  4,  2,  0,  2,  0,  0,  0,   0,   0,   0,    0,    0,   0, 	   31,	 24,     0,		  PLL_DRR0PAT, 0xd1303333 , 20);
SUNXI_CLK_FACTORS(			pll_periph0,8,  5,  4,  2,  0,  0,  0,  0,  0,   0,   0,   0,    0,    0,   0,     31,    0,     0,       0,        	0);
SUNXI_CLK_FACTORS(			pll_isp,    8,  7,  0,  0,  0,  4,  0,  0,  0,   0,   0,   0,    1,    25,  24,    31,   20,     0,       PLL_ISPPAT,  0xd1303333);
SUNXI_CLK_FACTORS(			pll_periph1,8,  5,  4,  2,  0,  0,  0,  0,  0,   0,   0,   0,    0,    0,   0,     31,   20,     0,       PLL_PERI1PAT,0xd1303333);
SUNXI_CLK_FACTORS_UPDATE(	pll_ddr1,   8,  6,  0,  0,  0,  2,  0,  0,  0,   0,   0,   0,    0,    0,   0, 	   31,	 24,     0,		  PLL_DDR1PAT, 0xf1303333 , 30);

static int get_factors_pll_cpu(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
   int index;
    u64 tmp_rate;
    if(!factor)
        return -1;
    tmp_rate = rate>pllcpu_max ? pllcpu_max : rate;
    do_div(tmp_rate, 1000000);
    index = tmp_rate;
 		if(sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_cpu,factor, factor_pllcpu_tbl,index,sizeof(factor_pllcpu_tbl)/sizeof(struct sunxi_clk_factor_freq)))
 			return -1;
    return 0;
}

static int get_factors_pll_ddr0(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
	int index;
	u64 tmp_rate;
	if (!factor)
		return -1;
	tmp_rate = rate > pllddr0_max ? pllddr0_max : rate;
	do_div(tmp_rate, 1000000);
	index = tmp_rate;
	if (sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_ddr0, factor,
					 factor_pllddr0_tbl, index,
					 sizeof(factor_pllddr0_tbl)/sizeof(struct sunxi_clk_factor_freq)))
		return -1;

	return 0;
}
static int get_factors_pll_ddr1(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
	int index;
	u64 tmp_rate;
	if (!factor)
		return -1;
	tmp_rate = rate > pllddr1_max ? pllddr1_max : rate;
	do_div(tmp_rate, 1000000);
	index = tmp_rate;
	if (sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_ddr1, factor,
					 factor_pllddr1_tbl, index,
					 sizeof(factor_pllddr1_tbl)/sizeof(struct sunxi_clk_factor_freq)))
		return -1;

	return 0;
}
static int get_factors_pll_periph0(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
   int index;
    u64 tmp_rate;
    if(!factor)
        return -1;
    tmp_rate = rate>pllperiph0_max ? pllperiph0_max : rate;
    do_div(tmp_rate, 1000000);
    index = tmp_rate;
    if(sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_periph0,factor, factor_pllperiph0_tbl,index,sizeof(factor_pllperiph0_tbl)/sizeof(struct sunxi_clk_factor_freq)))
        return -1;
    return 0;
}
static int get_factors_pll_periph1(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
   int index;
    u64 tmp_rate;
    if(!factor)
        return -1;
    tmp_rate = rate>pllperiph1_max ? pllperiph1_max : rate;
    do_div(tmp_rate, 1000000);
    index = tmp_rate;
        if(sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_periph1,factor, factor_pllperiph1_tbl,index,sizeof(factor_pllperiph1_tbl)/sizeof(struct sunxi_clk_factor_freq)))
 			return -1;
    return 0;
}
static int get_factors_pll_audio(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
    if(rate == 22579200) {
        factor->factorn = 6;
        factor->factorm = 0;
        factor->factorp = 7;
		sunxi_clk_factor_pll_audio.sdmval = 0xc0010d84;
    } else if(rate == 24576000) {
        factor->factorn = 13;
        factor->factorm = 0;
        factor->factorp = 13;
		sunxi_clk_factor_pll_audio.sdmval = 0xc000ac02;
    } else
        return -1;

    return 0;
}

static int get_factors_pll_video(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate;
    int index;
    if(!factor)
        return -1;

    tmp_rate = rate>pllvideo_max ? pllvideo_max : rate;
    do_div(tmp_rate, 1000000);
    index = tmp_rate;
 		if(sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_video,factor, factor_pllvideo_tbl,index,sizeof(factor_pllvideo_tbl)/sizeof(struct sunxi_clk_factor_freq)))
 			return -1;
    if(rate == 297000000) {
        factor->frac_mode = 0;
        factor->frac_freq = 1;
        factor->factorm = 0;
    }
    else if(rate == 270000000) {
        factor->frac_mode = 0;
        factor->frac_freq = 0;
        factor->factorm = 0;
    } else {
        factor->frac_mode = 1;
        factor->frac_freq = 0;
    }

    return 0;
}
static int get_factors_pll_isp(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate;
    int index;
    if(!factor)
        return -1;

    tmp_rate = rate>pllisp_max ? pllisp_max : rate;
    do_div(tmp_rate, 1000000);
    index = tmp_rate;
    if(sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_isp,factor, factor_pllisp_tbl,index,sizeof(factor_pllisp_tbl)/sizeof(struct sunxi_clk_factor_freq)))
        return -1;
    if(rate == 297000000) {
        factor->frac_mode = 0;
        factor->frac_freq = 1;
        factor->factorm = 0;
    }
    else if(rate == 270000000) {
        factor->frac_mode = 0;
        factor->frac_freq = 0;
        factor->factorm = 0;
    } else {
        factor->frac_mode = 1;
        factor->frac_freq = 0;
    }

    return 0;
}
static int get_factors_pll_ve(u32 rate, u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate;
    int index;
    if(!factor)
        return -1;

    tmp_rate = rate>pllve_max ? pllve_max : rate;
    do_div(tmp_rate, 1000000);
    index = tmp_rate;
    if(sunxi_clk_get_common_factors_search(&sunxi_clk_factor_pll_ve,factor, factor_pllve_tbl,index,sizeof(factor_pllve_tbl)/sizeof(struct sunxi_clk_factor_freq)))
        return -1;
    if(rate == 297000000) {
        factor->frac_mode = 0;
        factor->frac_freq = 1;
        factor->factorm = 0;
    }
    else if(rate == 270000000) {
        factor->frac_mode = 0;
        factor->frac_freq = 0;
        factor->factorm = 0;
    } else {
        factor->frac_mode = 1;
        factor->frac_freq = 0;
    }

    return 0;
}

static unsigned long calc_rate_pll_cpu(u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate = (parent_rate?parent_rate:24000000);
    tmp_rate = tmp_rate * (factor->factorn+1) * (factor->factork+1);
    do_div(tmp_rate, (factor->factorm+1) * (1 << factor->factorp));
    return (unsigned long)tmp_rate;
}

static unsigned long calc_rate_pll_audio(u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate = (parent_rate?parent_rate:24000000);
    if((factor->factorn == 6) && (factor->factorm == 0) && (factor->factorp == 7))
        return 22579200;
    else if((factor->factorn == 13) && (factor->factorm == 0) && (factor->factorp == 13))
        return 24576000;
    else
    {
        tmp_rate = tmp_rate * (factor->factorn+1);
        do_div(tmp_rate, (factor->factorm+1) * (factor->factorp+1));
        return (unsigned long)tmp_rate;
    }
}

static unsigned long calc_rate_media(u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate = (parent_rate?parent_rate:24000000);
    if(factor->frac_mode == 0)
    {
        if(factor->frac_freq == 1)
          return 297000000;
        else
          return 270000000;
    }
    else
    {
        tmp_rate = tmp_rate * (factor->factorn+1);
        do_div(tmp_rate, factor->factorm+1);
        return (unsigned long)tmp_rate;
    }
}

static unsigned long calc_rate_pll_ddr0(u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate = (parent_rate?parent_rate:24000000);
    tmp_rate = tmp_rate * (factor->factorn+1) * (factor->factork+1);
    do_div(tmp_rate, factor->factorm+1);
    return (unsigned long)tmp_rate;
}
static unsigned long calc_rate_pll_ddr1(u32 parent_rate, struct clk_factors_value *factor)
{
    u64 tmp_rate = (parent_rate?parent_rate:24000000);
    tmp_rate = tmp_rate * (factor->factorn+1) ;
    do_div(tmp_rate, factor->factorm+1);
    return (unsigned long)tmp_rate;
}
static unsigned long calc_rate_pll_periph(u32 parent_rate, struct clk_factors_value *factor)
{
    return (unsigned long)(parent_rate?(parent_rate/2):12000000) * (factor->factorn+1) * (factor->factork+1);
}

static const char *hosc_parents[] = {"hosc"};
struct factor_init_data sunxi_factos[] = {
    /* name         parent        parent_num, flags       reg       lock_reg     lock_bit    config                          get_factors          calc_rate       priv_ops*/
    {"pll_cpu",     hosc_parents, 1,CLK_GET_RATE_NOCACHE|CLK_RATE_FLAT_FACTORS|CLK_RATE_FLAT_DELAY, PLL_CPU,    PLL_CPU,     LOCKBIT(28),&sunxi_clk_factor_pll_cpu,    &get_factors_pll_cpu,    &calc_rate_pll_cpu     ,(struct clk_ops*)NULL},
    {"pll_audio",   hosc_parents, 1,          0,          PLL_AUDIO,  PLL_AUDIO,   LOCKBIT(28),&sunxi_clk_factor_pll_audio,  &get_factors_pll_audio,  &calc_rate_pll_audio   ,(struct clk_ops*)NULL},
    {"pll_video",   hosc_parents, 1,          0,          PLL_VIDEO,  PLL_VIDEO,   LOCKBIT(28),&sunxi_clk_factor_pll_video,  &get_factors_pll_video,  &calc_rate_media       ,(struct clk_ops*)NULL},
    {"pll_ve",      hosc_parents, 1,          0,          PLL_VE,     PLL_VE,      LOCKBIT(28),&sunxi_clk_factor_pll_ve,     &get_factors_pll_ve,     &calc_rate_media       ,(struct clk_ops*)NULL},
    {"pll_ddr0",    hosc_parents, 1,CLK_GET_RATE_NOCACHE, PLL_DDR0,   PLL_DDR0,    LOCKBIT(28),&sunxi_clk_factor_pll_ddr0,   &get_factors_pll_ddr0,   &calc_rate_pll_ddr0    ,(struct clk_ops*)NULL},
    {"pll_periph0", hosc_parents, 1,          0,          PLL_PERIPH0,PLL_PERIPH0, LOCKBIT(28),&sunxi_clk_factor_pll_periph0,&get_factors_pll_periph0,&calc_rate_pll_periph  ,(struct clk_ops*)NULL},
    {"pll_isp",     hosc_parents, 1,          0,          PLL_ISP,     PLL_ISP,    LOCKBIT(28),&sunxi_clk_factor_pll_isp,    &get_factors_pll_isp,    &calc_rate_media       ,(struct clk_ops*)NULL},
    {"pll_periph1", hosc_parents, 1,          0,          PLL_PERIPH1,PLL_PERIPH1, LOCKBIT(28),&sunxi_clk_factor_pll_periph1,&get_factors_pll_periph1,&calc_rate_pll_periph  ,(struct clk_ops*)NULL},
    {"pll_ddr1",    hosc_parents, 1,CLK_GET_RATE_NOCACHE, PLL_DDR1,   PLL_DDR1,    LOCKBIT(28),&sunxi_clk_factor_pll_ddr1,   &get_factors_pll_ddr1,   &calc_rate_pll_ddr1    ,(struct clk_ops*)NULL},
};


struct periph_init_data {
    const char          *name;
    unsigned long       flags;
    const char          **parent_names;
    int                 num_parents;
    struct sunxi_clk_periph *periph;
};

static const char *cpu_parents[] = {"losc", "hosc", "pll_cpu", "pll_cpu"};
static const char *axi_parents[] = {"cpu"};
static const char *pll_periphahb0_parents[] = {"pll_periph0"};
static const char *ahb0_parents[] = {"losc", "hosc", "axi", "pll_periphahb0"};
static const char *apb0_parents[] = {"ahb0"};
static const char *apb1_parents[] = {"losc", "hosc", "pll_periph0", "pll_periph0"};
static const char *periph0_parents[] = {"hosc", "pll_periph0","",""};
static const char *periph_parents[] = {"hosc", "pll_periph0","pll_periph1",""};
static const char *i2s_parents[] = {"pll_audiox8", "pll_audiox4", "pll_audiox2", "pll_audio"};
static const char *display_parents[] = {"pll_video", "", "pll_periph0", "", "", "","",""};
static const char *csi_s_parents[] = {"pll_video", "pll_isp", "", "", "", "","",""};
static const char *csi_m_parents[] = {"hosc", "pll_video", "pll_periph1", "pll_periph0", "", "","",""};
static const char *mbus_parents[] = {"hosc", "pll_periph0x2", "pll_ddr0", ""};
static const char *mipicsi_parents[] = {"pll_video", "pll_periph0", "pll_isp",""};
static const char *adda_parents[] = {"pll_audio"};
static const char *ve_parents[] = {"pll_ve"};
static const char *lvds_parents[] = {"tcon"};
static const char *ahb0mod_parents[] = {"ahb0"};
static const char *apb0mod_parents[] = {"apb0"};
static const char *apb1mod_parents[] = {"apb1"};
static const char *sdmmc2_parents[] = {"sdmmc2mod"};
static const char *ahb1_parents[] = {"ahb0" , "pll_periph0d2" , "" , ""};
static const char *ahb1mod_parents[] = { "ahb1"};

struct sunxi_clk_comgate com_gates[]={
{"csi",      0,  0x3,    BUS_GATE_SHARE|RST_GATE_SHARE|MBUS_GATE_SHARE, 0},
};
/*
SUNXI_CLK_PERIPH(name,    mux_reg,    mux_shift, mux_width, div_reg,    div_mshift, div_mwidth, div_nshift, div_nwidth, gate_flags, enable_reg, reset_reg, bus_gate_reg, drm_gate_reg, enable_shift, reset_shift, bus_gate_shift, dram_gate_shift, lock,com_gate,com_gate_off)
*/
SUNXI_CLK_PERIPH(cpu,     CPU_CFG,    16,        2,         0,          0,          0,          0,          0,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(axi,     0,           0,        0,         CPU_CFG,    0,          2,          0,          0,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(pll_periphahb0,  0,   0,        0,         AHB1_CFG,   6,          2,          0,          0,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(ahb0,    AHB1_CFG,   12,        2,         AHB1_CFG,   0,          0,          4,          2,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(apb0,    0,           0,        0,         AHB1_CFG,   0,          0,          8,          2,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(apb1,    APB2_CFG,   24,        2,         APB2_CFG,   0,          5,         16,          2,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(ahb1,    AHB2_CFG,    0,        2,         0,   		0,          0,          0,          0,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(sdmmc0,  SD0_CFG,    24,        2,         SD0_CFG,    0,          4,         16,          2,          0,          SD0_CFG,    BUS_RST0,  BUS_GATE0,    0,           31,            8,           8,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(sdmmc1,  SD1_CFG,    24,        2,         SD1_CFG,    0,          4,         16,          2,          0,          SD1_CFG,    BUS_RST0,  BUS_GATE0,    0,           31,            9,           9,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(sdmmc2mod,SD2_CFG,   24,        2,         SD2_CFG,    30,         1,          0,          0,          0,          0,          0,         0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(sdmmc2,  0,           0,        0,         SD2_CFG,    0,          4,         16,          2,          0,          SD2_CFG,    BUS_RST0,  BUS_GATE0,    0,           31,           10,          10,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(ss,      SS_CFG,     24,        2,         SS_CFG,     0,          4,         16,          2,          0,          SS_CFG,     BUS_RST0,  BUS_GATE0,    0,           31,            5,           5,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(spi0,    SPI0_CFG,   24,        2,         SPI0_CFG,   0,          4,         16,          2,          0,          SPI0_CFG,   BUS_RST0,  BUS_GATE0,    0,           31,           20,          20,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(i2s0,    I2S0_CFG,   16,        2,         0,          0,          0,          0,          0,          0,          I2S0_CFG,   BUS_RST3,  BUS_GATE2,    0,           31,           12,          12,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(usbohci, 0,           0,        0,         0,          0,          0,          0,          0,          0,          USB_CFG,    BUS_RST0,  BUS_GATE0,    DRAM_GATE,   16,           29,          29,             18,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(usbehci, 0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST0,  BUS_GATE0,    DRAM_GATE,    0,           26,          26,             17,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(usbotg,  0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST0,  BUS_GATE0,    0,            0,           24,          24,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(usbphy0, 0,           0,        0,         0,          0,          0,          0,          0,          0,          USB_CFG,    USB_CFG,   0,            0,            8,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(de,      DE_CFG,     24,        3,         DE_CFG,     0,          4,          0,          0,          0,          DE_CFG,     BUS_RST1,  BUS_GATE1,    0,           31,           12,          12,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(tcon,    TCON_CFG,   24,        3,         TCON_CFG,   0,          4,          0,          0,          0,          TCON_CFG,   BUS_RST1,  BUS_GATE1,    0,           31,            4,           4,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(csi0_s,  0,  		   0,        0,         0,          0,          0,          0,          0,          0,          CSI0_CFG,   BUS_RST1,  BUS_GATE1,    DRAM_GATE,   31,            8,           8,              1,               &clk_lock,&com_gates[0],    0);
SUNXI_CLK_PERIPH(csi0_m,  CSI0_CFG,    8,        3,         CSI0_CFG,   0,          5,          0,          0,          0,          CSI0_CFG,   BUS_RST1,  BUS_GATE1,    DRAM_GATE,   15,            8,           8,              1,               &clk_lock,&com_gates[0],    1);
SUNXI_CLK_PERIPH(csi1_s,  CSI1_CFG,   24,        3,         CSI1_CFG,  16,          4,          0,          0,          0,          CSI1_CFG,   BUS_RST1,  BUS_GATE1,    DRAM_GATE,   31,            8,           8,              1,               &clk_lock,&com_gates[0],    2);
SUNXI_CLK_PERIPH(csi1_m,  CSI1_CFG,    8,        3,         CSI1_CFG,   0,          5,          0,          0,          0,          CSI1_CFG,   BUS_RST1,  BUS_GATE1,    DRAM_GATE,   15,            8,           8,              1,               &clk_lock,&com_gates[0],    3);
SUNXI_CLK_PERIPH(ve,      0,           0,        0,         VE_CFG,    16,          3,          0,          0,          0,          VE_CFG,     BUS_RST1,  BUS_GATE1,    DRAM_GATE,   31,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(adda,    0,           0,        0,         0,          0,          0,          0,          0,          0,          ADDA_CFG,   BUS_RST3,   BUS_GATE2,   0,           31,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(avs,     0,           0,        0,         0,          0,          0,          0,          0,          0,          AVS_CFG,    0,         0,            0,           31,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(mbus,    MBUS_CFG,   24,        2,         MBUS_CFG,   0,          3,          0,          0,          0,          MBUS_CFG,   0,         0,            0,           31,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(mipicsi, MIPI_CSI,   24,        2,         MIPI_CSI,   0,          4,          0,          0,          0,          MIPI_CSI,   0,         0,            0,           31,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(gmac,    0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST0,  BUS_GATE0,    0,            0,           17,          17,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(dma,     0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST0,  BUS_GATE0,    0,            0,            6,           6,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(pio,     0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          0,         BUS_GATE2,    0,            0,            0,           5,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(uart0,   0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST4,  BUS_GATE3,    0,            0,           16,          16,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(uart1,   0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST4,  BUS_GATE3,    0,            0,           17,          17,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(uart2,   0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST4,  BUS_GATE3,    0,            0,           18,          18,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(twi0,    0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST4,  BUS_GATE3,    0,            0,            0,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(twi1,    0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST4,  BUS_GATE3,    0,            0,            1,           1,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(ephy,    0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST2,  BUS_GATE4,    0,            0,            2,           0,              0,               &clk_lock,NULL,             0);
SUNXI_CLK_PERIPH(lvds,    0,           0,        0,         0,          0,          0,          0,          0,          0,          0,          BUS_RST2,  0,            0,            0,            0,           0,              0,               &clk_lock,NULL,             0);

struct periph_init_data sunxi_periphs_init[] = {
    {"cpu",      CLK_GET_RATE_NOCACHE,	cpu_parents,      ARRAY_SIZE(cpu_parents),      &sunxi_clk_periph_cpu},
    {"axi",      0,       				axi_parents,      ARRAY_SIZE(axi_parents),      &sunxi_clk_periph_axi},
    {"pll_periphahb0",CLK_IGNORE_SYNCBOOT,pll_periphahb0_parents,  ARRAY_SIZE(pll_periphahb0_parents),  &sunxi_clk_periph_pll_periphahb0},
    {"ahb0",     0,       				ahb0_parents,     ARRAY_SIZE(ahb0_parents),     &sunxi_clk_periph_ahb0},
    {"apb0",     0,       				apb0_parents,     ARRAY_SIZE(apb0_parents),     &sunxi_clk_periph_apb0},
    {"apb1",     0,       				apb1_parents,     ARRAY_SIZE(apb1_parents),     &sunxi_clk_periph_apb1},
    {"ahb1",     0,       				ahb1_parents,     ARRAY_SIZE(ahb1_parents),     &sunxi_clk_periph_ahb1},
    {"sdmmc0",   0,       				periph_parents,   ARRAY_SIZE(periph_parents),   &sunxi_clk_periph_sdmmc0},
    {"sdmmc1",   0,       				periph_parents,   ARRAY_SIZE(periph_parents),   &sunxi_clk_periph_sdmmc1},
    {"sdmmc2mod",0,         			periph_parents,   ARRAY_SIZE(periph_parents),   &sunxi_clk_periph_sdmmc2mod},
    {"sdmmc2",   0,       			    sdmmc2_parents,   ARRAY_SIZE(sdmmc2_parents),   &sunxi_clk_periph_sdmmc2},
    {"ss",       0,       				periph0_parents,  ARRAY_SIZE(periph0_parents),  &sunxi_clk_periph_ss},
    {"spi0",     0,       				periph_parents,   ARRAY_SIZE(periph_parents),   &sunxi_clk_periph_spi0},
    {"i2s0",     0,       				i2s_parents,      ARRAY_SIZE(i2s_parents),      &sunxi_clk_periph_i2s0},
    {"usbohci",  0,       				ahb0mod_parents,  ARRAY_SIZE(ahb0mod_parents),  &sunxi_clk_periph_usbohci},
    {"usbehci",  0,       				ahb0mod_parents,  ARRAY_SIZE(ahb0mod_parents),  &sunxi_clk_periph_usbehci},
    {"usbotg",   0,       				ahb0mod_parents,  ARRAY_SIZE(ahb0mod_parents),  &sunxi_clk_periph_usbotg},
    {"usbphy0",  0,       				hosc_parents,     ARRAY_SIZE(hosc_parents),     &sunxi_clk_periph_usbphy0},
    {"de",       0,       				display_parents,  ARRAY_SIZE(display_parents),  &sunxi_clk_periph_de},
    {"tcon",     0,       				display_parents,  ARRAY_SIZE(display_parents),  &sunxi_clk_periph_tcon},
    {"csi0_s",   0,       				hosc_parents,     ARRAY_SIZE(hosc_parents),     &sunxi_clk_periph_csi0_s},
    {"csi0_m",   0,       				csi_m_parents,    ARRAY_SIZE(csi_m_parents),    &sunxi_clk_periph_csi0_m},
    {"csi1_s",   0,       				csi_s_parents,    ARRAY_SIZE(csi_s_parents),    &sunxi_clk_periph_csi1_s},
    {"csi1_m",   0,       				csi_m_parents,    ARRAY_SIZE(csi_m_parents),    &sunxi_clk_periph_csi1_m},
    {"ve",       0,       				ve_parents,       ARRAY_SIZE(ve_parents),       &sunxi_clk_periph_ve},
    {"lvds",     0,        			    lvds_parents,     ARRAY_SIZE(lvds_parents),     &sunxi_clk_periph_lvds},
    {"adda",   0,       				adda_parents,     ARRAY_SIZE(adda_parents),     &sunxi_clk_periph_adda},
    {"avs",      0,       				hosc_parents,     ARRAY_SIZE(hosc_parents),     &sunxi_clk_periph_avs},
    {"mbus",     0,       				mbus_parents,     ARRAY_SIZE(mbus_parents),     &sunxi_clk_periph_mbus},
    {"mipicsi",  0,       				mipicsi_parents,  ARRAY_SIZE(mipicsi_parents),  &sunxi_clk_periph_mipicsi},
    {"gmac",      0,       				ahb1mod_parents,  ARRAY_SIZE(ahb1mod_parents),  &sunxi_clk_periph_gmac},
    {"ephy",      0,       				ahb0mod_parents,  ARRAY_SIZE(ahb0mod_parents),  &sunxi_clk_periph_ephy},
    {"dma",      0,       				ahb0mod_parents,  ARRAY_SIZE(ahb0mod_parents),  &sunxi_clk_periph_dma},
    {"pio",      0,       				apb0mod_parents,  ARRAY_SIZE(apb0mod_parents),  &sunxi_clk_periph_pio},
    {"twi0",     0,       				apb1mod_parents,  ARRAY_SIZE(apb1mod_parents),  &sunxi_clk_periph_twi0},
    {"twi1",     0,       				apb1mod_parents,  ARRAY_SIZE(apb1mod_parents),  &sunxi_clk_periph_twi1},
    {"uart0",    0,       				apb1mod_parents,  ARRAY_SIZE(apb1mod_parents),  &sunxi_clk_periph_uart0},
    {"uart1",    0,       				apb1mod_parents,  ARRAY_SIZE(apb1mod_parents),  &sunxi_clk_periph_uart1},
    {"uart2",    0,       				apb1mod_parents,  ARRAY_SIZE(apb1mod_parents),  &sunxi_clk_periph_uart2},
};

void __init sunxi_init_clocks(void)
{
    int     i;
	struct clk *clk;
    struct factor_init_data *factor;
    struct periph_init_data *periph;

#ifdef CONFIG_SUNXI_CLK_DUMMY_DEBUG
		sunxi_clk_base = &dummy_reg[0];
		dummy_reg_init();
#else
    /* get clk register base address */
    sunxi_clk_base = IO_ADDRESS(0x01c20000);
#endif
    sunxi_clk_factor_initlimits();

    /* register oscs */
    clk = clk_register_fixed_rate(NULL, "losc", NULL, CLK_IS_ROOT, 32768);
    clk_register_clkdev(clk, "losc", NULL);

    clk = clk_register_fixed_rate(NULL, "hosc", NULL, CLK_IS_ROOT, 24000000);
    clk_register_clkdev(clk, "hosc", NULL);

    /* register normal factors, based on sunxi factor framework */
    for(i=0; i<ARRAY_SIZE(sunxi_factos); i++) {
        factor = &sunxi_factos[i];
        clk = sunxi_clk_register_factors(NULL,  sunxi_clk_base, &clk_lock,factor);
        clk_register_clkdev(clk, factor->name, NULL);
    }

    /* register fixed factors, based on clk-fixed-factor framework, such as pllx2 for ex. */
    clk = clk_register_fixed_factor(NULL, "pll_audiox8", "pll_audio", 0, 8, 1);
    clk_register_clkdev(clk, "pll_audiox8", NULL);

    clk = clk_register_fixed_factor(NULL, "pll_audiox4", "pll_audio", 0, 8, 2);
    clk_register_clkdev(clk, "pll_audiox4", NULL);

    clk = clk_register_fixed_factor(NULL, "pll_audiox2", "pll_audio", 0, 8, 4);
    clk_register_clkdev(clk, "pll_audiox2", NULL);

    clk = clk_register_fixed_factor(NULL, "pll_videox2", "pll_video", 0, 2, 1);
    clk_register_clkdev(clk, "pll_videox2", NULL);

    clk = clk_register_fixed_factor(NULL, "pll_periph0x2", "pll_periph0", 0, 2, 1);
    clk_register_clkdev(clk, "pll_periph0x2", NULL);

	clk = clk_register_fixed_factor(NULL, "pll_periph0d2", "pll_periph0", 0, 1, 2);
    clk_register_clkdev(clk, "pll_periph0d2", NULL);

	clk = clk_register_fixed_factor(NULL, "hoscd2", "hosc", 0, 1, 2);
    clk_register_clkdev(clk, "hoscd2", NULL);

    /* register periph clock */
    for(i=0; i<ARRAY_SIZE(sunxi_periphs_init); i++) {
        periph = &sunxi_periphs_init[i];
		clk = sunxi_clk_register_periph(periph->name, periph->parent_names,
					periph->num_parents,periph->flags, sunxi_clk_base, periph->periph);
        clk_register_clkdev(clk, periph->name, NULL);
    }

	clk = clk_get(NULL,"ahb1");
    if(!clk || IS_ERR(clk))
        printk("Error not get clk ahb1\n");
    else
	{
		struct clk  *parent_clk = clk_get(NULL,"pll_periph0d2");
        if(!parent_clk || IS_ERR(parent_clk))
        {
            printk("%d Error not get clk pll_periph0d2\n" , __LINE__);
        }
        else
        {
			printk("%d ahb1 set parent pll_periph0d2\n" , __LINE__);
			clk_set_parent(clk,parent_clk);
			clk_put(parent_clk);
		}
		
		clk_put(clk);
	}	
	
    clk_add_alias("pll1",NULL,"pll_cpu",NULL);
    clk_add_alias("pll2",NULL,"pll_audio",NULL);
    clk_add_alias("pll3",NULL,"pll_video",NULL);
    clk_add_alias("pll4",NULL,"pll_ve",NULL);
    clk_add_alias("pll5",NULL,"pll_ddr0",NULL);
    clk_add_alias("pll6",NULL,"pll_periph0",NULL);
    clk_add_alias("pll6ahb0",NULL,"pll_periphahb0",NULL);
#ifdef CONFIG_COMMON_CLK_ENABLE_SYNCBOOT_EARLY
	clk_syncboot();
#endif
}

